package org.jef.core.mask;

import jef.tools.string.RegexpUtils;
import org.apache.commons.lang3.StringUtils;

import java.util.Arrays;

/**
 * @version 1.0
 * @description: 通用模糊策略类
 * @author: ChengZhi 0336
 * @create: 2024-09-24 10:05
 **/
public class CommonMaskStrategy implements EvalStrategy{

    private static final String REGEX_EXPRESSION = "^([\\*,#])?(\\d*)?(\\(-?\\d*)\\,(-?\\d*\\))$";

    /**
     * 默认替换所有字符为"*"
     */
    private static final String DEFAULT_MASK_EXPRESSION = "*(,)";

    /**
     * 默认替换符为"*"
     */
    private static final String DEFAULT_MASK_CHAR = "*";

    private String[] parsedExpressions;

    /**
     * 使用默认替换表达式，即替换所有字符为"*"
     */
    public CommonMaskStrategy() {
        parsedExpressions = parse(DEFAULT_MASK_EXPRESSION);
    }

    /**
     * @param expression
     *            表达式格式为：替换符fixed(leftOffset, rightOffset)<br>
     *            替换符仅支持*和#, <br>
     *            替换符, fixed, leftOffset, rightOffset 均可以不指定，但位置需保留；<br>
     *            leftOffset 支持负数，负数表示距离末尾的偏移位置(即替换末几位字符)，负数时rightOffset值无效，<br>
     *            leftOffset 不指定则默认为0; <br>
     *            rightOffset 支持负数，负数表示距离末尾的偏移位置。<br>
     *            举例：<br>
     *            "(,)", 表示将所有字符替换为*<br>
     *            "#(,)", 表示将所有字符替换为#<br>
     *            "#3(,)", 表示将所有字符替换为3个#(即###)<br>
     *            "#(2,8)", 表示将2-8位字符替换为#<br>
     *            "#3(2,8)", 表示将2-8位字符替换3个#(即###)<br>
     *            "#(2,)", 表示将第2个字符之后的所有字符替换为#<br>
     *            "#(-2,)", 表示将末2位替换为#<br>
     *            "#(,8)", 表示将1-8位字符替换为#(即替换第9位之前的所有字符)<br>
     *            "#(,-2)", 表示将末2位之前的所有字符替换为#<br>
     *            "#(3,-2)", 表示将第3位之后、末2位之前的所有字符替换为#
     */
    public CommonMaskStrategy(String expression) {
        parsedExpressions = parse(expression);
        if (parsedExpressions == null) {
            throw new IllegalArgumentException("Illeagal mask expression:" + expression);
        }
        if (parsedExpressions[0] == null) {
            parsedExpressions[0] = DEFAULT_MASK_CHAR;
        }
    }

    private String[] parse(String expression) {
        return RegexpUtils.getMatcherResult(expression, REGEX_EXPRESSION, true);
    }

    public String eval(Object srcValue, Object thisEntityObj) {
        if (srcValue == null)
            return null;

        String value = toStringValue(srcValue);
        return mask(value, parseRange(value, parsedExpressions[2], parsedExpressions[3]));
    }

    private String toStringValue(Object srcValue) {
        return srcValue instanceof String ? (String) srcValue : srcValue.toString();
    }

    private Integer[] parseRange(String value, String leftOffset, String rightOffset) {
        // 初始化下界为0, 上界为value.length
        Integer[] result = new Integer[] { 0, value.length() };

        // 计算起始位置
        String boundaryValue = leftOffset.substring(1);
        if (StringUtils.isNotBlank(boundaryValue)) {
            result[0] = Integer.valueOf(boundaryValue);

            // 起始位置为负数时，忽略rightOffset
            if (result[0] < 0) {
                result[0] = value.length() + result[0];
                return result;
            }
        }

        // 计算替换长度
        boundaryValue = rightOffset.substring(0, rightOffset.length() - 1);
        if (StringUtils.isNotBlank(boundaryValue)) {
            int rightIndex = Integer.valueOf(boundaryValue);
            result[1] = calculateReplaceLength(value.length(), result[0], rightIndex);
        }

        return result;
    }

    private int calculateReplaceLength(int totalLength, int leftIndex, int rightIndex) {
        int len = rightIndex;
        if (rightIndex < 0) {
            len = totalLength - leftIndex + rightIndex;
        } else {
            len = rightIndex - leftIndex;
        }
        return len;
    }

    private String mask(String srcValue, Integer[] ranges) {
        if (StringUtils.isBlank(parsedExpressions[1])) {
            return this.replace(srcValue, parsedExpressions[0].charAt(0), ranges[0], ranges[1]);
        } else {
            return this.replace(srcValue, parsedExpressions[0].charAt(0), Integer.parseInt(parsedExpressions[1]), ranges[0], ranges[1]);
        }
    }

    /**
     * 把[offset,offset+length)范围内的字符替换成replacement
     *
     * @param text
     * @param replacement
     * @param offset
     *            :[0,text.length-1]
     * @param length
     *            :<=text.length
     * @return 替换后的字符串
     */
    public static String replace(String text, char replacement, int offset, int length) {
        if (StringUtils.isEmpty(text) || offset >= text.length() || length <= 0)
            return text;
        if (offset < 0)
            offset = 0;
        int end = offset + length;
        if (end > text.length())
            end = text.length();

        char chars[] = text.toCharArray();
        for (int i = offset; i < end; i++)
            chars[i] = replacement;
        return new String(chars);
    }

    /**
     * 把[offset,offset+length)范围内的字符替换成fixed个replacement
     *
     * @param text
     *            原字符串
     * @param replacement
     *            替换符
     * @param fixed
     *            个数
     * @param offset
     *            替换起始位置
     * @param length
     *            替换长度
     * @return
     */
    public static String replace(String text, char replacement, int fixed, int offset, int length) {
        if (StringUtils.isEmpty(text) || offset >= text.length() || length <= 0)
            return text;
        if (offset < 0)
            offset = 0;
        int end = offset + length;
        if (end > text.length())
            end = text.length();

        String left = text.substring(0, offset);
        String right = text.substring(end);

        char chars[] = new char[fixed];
        for (int i = 0; i < fixed; i++)
            chars[i] = replacement;

        return concat(left, new String(chars), right);
    }

    /**
     * 合并多个String,在参数为3个和以内时请直接使用String.concat。
     * 5个和超过5个String相加后，concat方法性能急剧下降，此时此方法最快
     *
     * @param args
     * @return
     */
    private final static String concat(String... args) {
        if (args.length == 1)
            return args[0];
        int n = 0;
        for (String s : args) {
            if (s == null)
                continue;
            n += s.length();
        }
        StringBuilder sb = new StringBuilder(n);
        for (String s : args) {
            if (s == null)
                continue;
            sb.append(s);
        }
        return sb.toString();
    }

    public static void main(String[] args) {
        CommonMaskStrategy maskStategy = new CommonMaskStrategy();
        String[] ranges = maskStategy.parse("*(4,)");
        System.out.println(ranges[0] + ranges[1] + ranges[2]);
        ranges = maskStategy.parse("(4,6)");
        System.out.println(ranges[0] + ranges[1] + ranges[2]);
        System.out.println("parse(\"*(,)\"): " + Arrays.toString(maskStategy.parse("*(,)")));
        System.out.println("parse(\"*(4,6)\"): " + Arrays.toString(maskStategy.parse("*(4,6)")));
        System.out.println("parse(\"*(4,6]\"): " + Arrays.toString(maskStategy.parse("*(4,6]")));
        System.out.println("parse(\"*(4,]\"): " + Arrays.toString(maskStategy.parse("*(4,]")));
        System.out.println("parse(\"*[,6)\"): " + Arrays.toString(maskStategy.parse("*[,6)")));
        System.out.println("parse(\"&[,6)\"): " + Arrays.toString(maskStategy.parse("&[,6)")));
        System.out.println("parse(\"#3(0,6)\"): " + Arrays.toString(maskStategy.parse("#3(0,6)")));
        System.out.println("parse(\"#(0,-6)\"): " + Arrays.toString(maskStategy.parse("#(0,-6)")));
        System.out.println("parse(\"#3(0,-6)\"): " +
                Arrays.toString(maskStategy.parse("#3(0,-6)")));
        System.out.println("parse(\"#(-4,)\"): " + Arrays.toString(maskStategy.parse("#(-4,)")));
    }
}
